﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using SharedGenomics.Domain;
using SharedGenomics.Workbench.Core;
using SharedGenomics.Workbench.Models;
using SharedGenomics.Workbench.Services;
using SharedGenomics.DataTransfer;

using Rhino.Mocks;
using Rhino.Mocks.Constraints;

namespace SharedGenomics.Workbench.Tests.Models
{
    [TestClass]
    public class RemoteJobDetailsRepositoryTests
    {
        private MockRepository mocks;
        private IDataService service;
        private RemoteJobDetailsRepository repository;

        [TestInitialize]
        public void Setup()
        {
            this.mocks = new MockRepository();
            this.service = this.mocks.Stub<IDataService>();

            this.repository = new RemoteJobDetailsRepository(this.service);
        }

        [TestMethod]
        public void ShouldGetUserJobsFromService()
        {
            using (this.mocks.Record())
            {
                var jobs = new JobDetails[]
                {
                    new JobDetails { Id = 1 },
                    new JobDetails { Id = 2 }
                };

                this.service.Stub(s => s.GetUserJobs(null, null)).IgnoreArguments().Return(jobs.Just());
            }

            using (this.mocks.Playback())
            {
                //NOTE: all jobs should match spec
                var userJobs = this.repository.GetUserJobs(new JobSpecification());
                Assert.AreEqual(2, userJobs.Value.Count());
            }
        }

        [TestMethod]
        public void ShouldReturnNothing()
        {
            using (this.mocks.Record())
            {
                this.service.Stub(s => s.GetUserJobs(null, null)).IgnoreArguments().Return(new Maybe<JobDetails[]>());
            }

            using (this.mocks.Playback())
            {
                var userJobs = this.repository.GetUserJobs(new JobSpecification());
                Assert.IsFalse(userJobs.HasValue, "Repository returned value");
            }
        }

        [TestMethod]
        public void ShouldIncludeMatchingCachedJobs()
        {
            using (this.mocks.Record())
            {
                var firstJobs = new JobDetails[]
                {
                    new JobDetails { Id = 1, JobType = JobType.PassocBT },
                    new JobDetails { Id = 2, JobType = JobType.FastPepiBT }
                };

                var secondJobs = new JobDetails[]
                {
                    new JobDetails { Id = 3, JobType = JobType.PassocBT }
                };

                using (this.mocks.Ordered())
                {
                    this.service.Stub(s => s.GetUserJobs(null, null)).IgnoreArguments().Return(firstJobs.Just());
                    this.service.Stub(s => s.GetUserJobs(null, null)).IgnoreArguments().Return(secondJobs.Just());
                }
            }

            using (this.mocks.Playback())
            {
                var spec = new JobSpecification { JobType = JobType.PassocBT };
                var first = this.repository.GetUserJobs(spec);
                
                var second = this.repository.GetUserJobs(spec);
                Assert.AreEqual(1, first.Value.Count());
                Assert.AreEqual(2, second.Value.Count());
            }
        }

        [TestMethod]
        public void ShouldSendCurrentIdsToService()
        {
            using (this.mocks.Record())
            {
                var firstJobs = new JobDetails[]
                {
                    new JobDetails { Id = 1, JobType = JobType.PassocBT, Status = JobStatus.Finished },
                    new JobDetails { Id = 2, JobType = JobType.FastPepiBT, Status = JobStatus.Finished }
                };

                var secondJobs = new JobDetails[]
                {
                    new JobDetails { Id = 3, JobType = JobType.PassocBT }
                };

                using (this.mocks.Ordered())
                {
                    this.service.Stub(s => s.GetUserJobs(null, null)).IgnoreArguments().Return(firstJobs.Just());

                    //second call should include the ids of those returned by the first
                    this.service.Expect(s => s.GetUserJobs(null, null)).IgnoreArguments()
                        .Constraints(Is.Anything(), Property.Value("Length", 2))
                        .Return(secondJobs.Just());
                }
            }

            using (this.mocks.Playback())
            {
                var spec = new JobSpecification { JobType = JobType.PassocBT };

                var first = this.repository.GetUserJobs(spec);
                var second = this.repository.GetUserJobs(spec);
            }
        }

        [TestMethod]
        public void ShouldNotExcludeUnfinishedJobsInServiceCall()
        {
            using (this.mocks.Record())
            {
                var firstJobs = new JobDetails[]
                {
                    new JobDetails { Id = 1, JobType = JobType.PassocBT, Status = JobStatus.Running },
                    new JobDetails { Id = 2, JobType = JobType.FastPepiBT, Status = JobStatus.Finished }
                };

                var secondJobs = new JobDetails[]
                {
                    new JobDetails { Id = 3, JobType = JobType.PassocBT }
                };

                using (this.mocks.Ordered())
                {
                    this.service.Stub(s => s.GetUserJobs(null, null)).IgnoreArguments().Return(firstJobs.Just());

                    //second call should include the ids of those returned by the first *except* any that have not completed (id = 1 was running so this 
                    //should not be included)
                    this.service.Expect(s => s.GetUserJobs(null, null)).IgnoreArguments()
                        .Constraints(Is.Anything(), Property.Value("Length", 1))
                        .Return(secondJobs.Just());
                }
            }

            using (this.mocks.Playback())
            {
                var spec = new JobSpecification { JobType = JobType.PassocBT };

                var first = this.repository.GetUserJobs(spec);
                var second = this.repository.GetUserJobs(spec);
            }
        }
    }
}
